<template>
	<view class="uni-city-list" ref="list" id="list">
		<!-- #ifdef APP-NVUE -->
		<list class="uni-city-list__scroll" scrollable="true" show-scrollbar="false">
			<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-city-list-' + idx">
			<!-- #endif -->
				<!-- #ifndef APP-NVUE -->
				<scroll-view :scroll-into-view="scrollViewId" class="uni-city-list__scroll" scroll-y @scroll="onScroll">
					<view v-for="(list, idx) in lists" :key="idx" :id="'uni-city-list-' + idx">
					<!-- #endif -->
						<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
							@itemClick="onClick"></indexed-list-item>
					<!-- #ifndef APP-NVUE -->
					</view>
				</scroll-view>
				<!-- #endif -->
			<!-- #ifdef APP-NVUE -->
			</cell>
		</list>
		<!-- #endif -->
		<view class="uni-city-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
			@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
			@mouseleave.stop="mouseleave">
			<view v-for="(list, key) in lists" :key="key" class="uni-city-list__menu-item"
				:class="touchmoveIndex == key ? 'uni-city-list__menu--active' : ''">
				<text class="uni-city-list__menu-text"
					:class="touchmoveIndex == key ? 'uni-city-list__menu-text--active' : ''">{{ list.key }}</text>
			</view>
		</view>
		<view v-if="touchmove" class="uni-city-list__alert-wrapper">
			<text class="uni-city-list__alert">{{ lists[touchmoveIndex].key }}</text>
		</view>
	</view>
</template>
<script>
	import indexedListItem from './uni-city-list-item.vue'
	// #ifdef APP-NVUE
	const dom = weex.requireModule('dom');
	// #endif
	// #ifdef APP-PLUS
	function throttle(func, delay) {
		var prev = Date.now();
		return function() {
			var context = this;
			var args = arguments;
			var now = Date.now();
			if (now - prev >= delay) {
				func.apply(context, args);
				prev = Date.now();
			}
		}
	}

	function touchMove(e) {
		let pageY = e.touches[0].pageY
		let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
		if (this.touchmoveIndex === index) {
			return false
		}
		let item = this.lists[index]
		if (item) {
			// #ifndef APP-NVUE
			this.scrollViewId = 'uni-city-list-' + index
			this.touchmoveIndex = index
			// #endif
			// #ifdef APP-NVUE
			dom.scrollToElement(this.$refs['uni-city-list-' + index][0], {
				animated: false
			})
			this.touchmoveIndex = index
			// #endif
		}
	}
	const throttleTouchMove = throttle(touchMove, 40)
	// #endif

	/**
	 * IndexedList 索引列表
	 * @description 用于展示索引列表
	 * @tutorial https://ext.dcloud.net.cn/plugin?id=375
	 * @property {Boolean} showSelect = [true|false] 展示模式
	 * 	@value true 展示模式
	 * 	@value false 选择模式
	 * @property {Object} options 索引列表需要的数据对象
	 * @event {Function} click 点击列表事件 ，返回当前选择项的事件对象
	 * @example <uni-city-list options="" showSelect="false" @click=""></uni-city-list>
	 */
	export default {
		name: 'UniIndexedList',
		components: {
			indexedListItem
		},
		emits: ['click'],
		props: {
			options: {
				type: Array,
				default () {
					return []
				}
			},
			showSelect: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				lists: [],
				winHeight: 0,
				itemHeight: 0,
				winOffsetY: 0,
				touchmove: false,
				touchmoveIndex: 0,
				scrollViewId: '',
				touchmovable: true,
				loaded: false,
				isPC: false,
				currentIndex: 0,
				letterRects: [],
				lastScrollTop: 0,
				scrollTimer: null,
				isTouching: false,
				menuOffsetY: 0,
				menuHeight: 0,
				rectsReady: false,
				isFirstScroll: true,
			}
		},
		watch: {
			options: {
				handler: function() {
					this.setList()
				},
				deep: true
			}
		},
		mounted() {
			// #ifdef H5
			this.isPC = this.IsPC()
			// #endif
			setTimeout(() => {
				this.setList()
			}, 50)
			setTimeout(() => {
				this.loaded = true
			}, 300);
		},
		methods: {
			setList() {
				let index = 0;
				this.lists = []
				this.options.forEach((value, index) => {
					if (value.data.length === 0) {
						return
					}
					let indexBefore = index
					let items = value.data.map(item => {
						let obj = {}
						obj['key'] = value.letter
						obj['name'] = item
						obj['itemIndex'] = index
						index++
						obj.checked = item.checked ? item.checked : false
						return obj
					})
					this.lists.push({
						title: value.letter,
						key: value.letter,
						items: items,
						itemIndex: indexBefore
					})
				})

				this.isFirstScroll = true
				this.lastScrollTop = 0
				this.touchmoveIndex = 0

				this.initPositions()
			},
			initPositions() {
				setTimeout(() => {
					uni.createSelectorQuery()
						.in(this)
						.select('.uni-city-list__menu')
						.boundingClientRect()
						.exec(ret => {
							if (ret && ret[0]) {
								this.menuOffsetY = ret[0].top
								this.menuHeight = ret[0].height
								this.itemHeight = this.menuHeight / this.lists.length
							}
						})

					uni.createSelectorQuery()
						.in(this)
						.select('#list')
						.boundingClientRect()
						.exec(ret => {
							this.winOffsetY = ret[0].top
							this.winHeight = ret[0].height
							this.getLetterRects()
						})

					// #ifdef APP-NVUE
					dom.getComponentRect(this.$refs['list'], (res) => {
						this.winOffsetY = res.size.top
						this.winHeight = res.size.height
						this.getLetterRects()
					})
					// #endif
				}, 50)
			},
			getLetterRects() {
				// #ifndef APP-NVUE
				const query = uni.createSelectorQuery().in(this)
				this.lists.forEach((item, index) => {
					query.select(`#uni-city-list-${index}`).boundingClientRect()
				})
				query.select('.uni-city-list__scroll').boundingClientRect()
				query.exec(rects => {
					if (!rects || rects.length <= 1) return
					const scrollRect = rects.pop() // 获取scroll-view的位置信息
					this.letterRects = rects.map(rect => ({
						top: rect.top,
						bottom: rect.bottom,
						height: rect.height,
						offsetTop: rect.top - scrollRect.top // 相对于scroll-view的偏移
					}))

					// 确保位置信息正确获取后再允许滚动检测
					this.rectsReady = true
				})
				// #endif

				// #ifdef APP-NVUE
				let count = 0
				this.letterRects = new Array(this.lists.length)

				this.lists.forEach((item, index) => {
					const ref = this.$refs[`uni-city-list-${index}`]
					if (ref && ref[0]) {
						dom.getComponentRect(ref[0], (res) => {
							this.letterRects[index] = {
								top: res.size.top,
								bottom: res.size.top + res.size.height,
								height: res.size.height,
								offsetTop: res.size.top - this.winOffsetY
							}
							count++
							if (count === this.lists.length) {
								this.rectsReady = true
							}
						})
					}
				})
				// #endif
			},
			onScroll(e) {
				if (!this.letterRects.length || !this.rectsReady) return

				const scrollTop = e.detail.scrollTop

				if (this.isFirstScroll) {
					this.isFirstScroll = false
					this.updateIndexByScrollTop(scrollTop)
					return
				}

				if (this.isTouching) return

				this.updateIndexByScrollTop(scrollTop)
			},
			updateIndexByScrollTop(scrollTop) {
				let found = false

				for (let i = 0; i < this.letterRects.length; i++) {
					const rect = this.letterRects[i]
					const rectTop = rect.offsetTop
					const rectBottom = rectTop + rect.height

					if (scrollTop >= rectTop && scrollTop < rectBottom) {
						if (this.touchmoveIndex !== i) {
							this.touchmoveIndex = i
						}
						found = true
						break
					}
				}

				if (!found && scrollTop + this.winHeight >= this.letterRects[this.letterRects.length - 1].offsetTop) {
					this.touchmoveIndex = this.letterRects.length - 1
				}

				this.lastScrollTop = scrollTop
			},
			calculateTouchIndex(pageY) {
				const touchY = pageY - this.menuOffsetY
				let index = Math.floor((touchY / this.menuHeight) * this.lists.length)

				if (index < 0) index = 0
				if (index >= this.lists.length) index = this.lists.length - 1

				return index
			},
			touchStart(e) {
				this.touchmove = true
				this.isTouching = true
				let pageY = this.isPC ? e.pageY : e.touches[0].pageY

				let index = this.calculateTouchIndex(pageY)
				let item = this.lists[index]
				if (item) {
					this.touchmoveIndex = index
					this.scrollViewId = 'uni-city-list-' + index
					// #ifdef APP-NVUE
					dom.scrollToElement(this.$refs['uni-city-list-' + index][0], {
						animated: false
					})
					// #endif
				}
			},
			touchMove(e) {
				let pageY = this.isPC ? e.pageY : e.touches[0].pageY
				let index = this.calculateTouchIndex(pageY)

				if (this.touchmoveIndex === index) {
					return false
				}

				let item = this.lists[index]
				if (item) {
					this.touchmoveIndex = index
					this.scrollViewId = 'uni-city-list-' + index
					// #ifdef APP-NVUE
					dom.scrollToElement(this.$refs['uni-city-list-' + index][0], {
						animated: false
					})
					// #endif
				}
			},
			touchEnd() {
				this.touchmove = false
				if (this.scrollTimer) {
					clearTimeout(this.scrollTimer)
				}
				this.scrollTimer = setTimeout(() => {
					this.isTouching = false
				}, 100)
			},

			/**
			 * 兼容 PC @tian
			 */

			mousedown(e) {
				if (!this.isPC) return
				this.touchStart(e)
			},
			mousemove(e) {
				if (!this.isPC) return
				this.touchMove(e)
			},
			mouseleave(e) {
				if (!this.isPC) return
				this.touchEnd(e)
			},

			// #ifdef H5
			IsPC() {
				var userAgentInfo = navigator.userAgent;
				var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
				var flag = true;
				for (let v = 0; v < Agents.length - 1; v++) {
					if (userAgentInfo.indexOf(Agents[v]) > 0) {
						flag = false;
						break;
					}
				}
				return flag;
			},
			// #endif


			onClick(e) {
				let {
					idx,
					index
				} = e
				let obj = {}
				for (let key in this.lists[idx].items[index]) {
					obj[key] = this.lists[idx].items[index][key]
				}
				let select = []
				if (this.showSelect) {
					this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
					this.lists.forEach((value, idx) => {
						value.items.forEach((item, index) => {
							if (item.checked) {
								let obj = {}
								for (let key in this.lists[idx].items[index]) {
									obj[key] = this.lists[idx].items[index][key]
								}
								select.push(obj)
							}
						})
					})
				}
				this.$emit('click', {
					item: obj,
					select: select
				})
			}
		}
	}
</script>
<style lang="scss" scoped>
	.uni-city-list {
		position: absolute;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		padding: 30rpx;
		padding-bottom: 30rpx !important;
		padding-bottom: calc(30rpx + constant(safe-area-inset-bottom)) !important;
		padding-bottom: calc(30rpx + env(safe-area-inset-bottom)) !important;
	}

	.uni-city-list__scroll {
		flex: 1;
	}

	.uni-city-list__menu {
		width: 48rpx;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
	}

	.uni-city-list__menu-item {
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex: 1;
		align-items: center;
		justify-content: center;
		/* #ifdef H5 */
		cursor: pointer;
		/* #endif */
	}

	.uni-city-list__menu-text {
		font-size: 24rpx;
		text-align: center;
		color: #aaa;
	}

	.uni-city-list__menu--active {
		// background-color: rgb(200, 200, 200);
	}

	.uni-city-list__menu--active {}

	.uni-city-list__menu-text--active {
		border-radius: 32rpx;
		width: 32rpx;
		height: 32rpx;
		line-height: 32rpx;
		background-color: #14cc60;
		color: #fff;
	}

	.uni-city-list__alert-wrapper {
		position: absolute;
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}

	.uni-city-list__alert {
		width: 160rpx;
		height: 160rpx;
		border-radius: 160rpx;
		text-align: center;
		line-height: 160rpx;
		font-size: 70rpx;
		color: #fff;
		background-color: rgba(243, 243, 243, 0.5);
	}
</style>